home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
626-637
/
disk_629
/
rexxrmf
/
rexxrmf.lzh
/
rexxrmf.doc
< prev
next >
Wrap
Text File
|
1992-03-14
|
83KB
|
2,134 lines
RexxRMF.Library
(version 1.8 Mar. 1992)
The RexxRMF.library is a library which gives ARexx programs the ability
to maintain 'indexes' of small files for the purpose of providing fast
search/sorting and retrieval of data records.
The RexxRMF.library consists of several 'low' level functions that are
used to maintain a balanced binary tree (AVL-tree) to index your
data files. Built on top of the AVL-tree routines are 'high' level
functions to provide simple Record Management Facilities.
The record management functions allow for:
Variable length keys
Variable length records
Duplicate keys
Mulitple indexes (ie. you can use alternate keys)
Passing of record data directly to/from ARexx variables.
eg.
/* an example ARexx pgm might be */
x = addlib("RexxRMF.library",0,-30,0) /* make lib available */
index = open_rmf("phonelist") /* open index file */
myrecord = "lastname firstname phonenumber" /* define record */
x = writech(stdout,"Enter Name:") /* get search key */
parse pull name
x = read_rmf_record(index,0,myrecord,name,'K') /* read record */
if x = 1 then
say firstname lastname phonenumber /* print record */
else
say name "Not Found
x = close_rmf(index,1)
exit
In order to use the RexxRMF.library simply place it in your LIBS:
directory.
RexxRMF.Library
RexxRMF library builds and maintains an AVL-tree in memory to index your
data files. Since the entire tree is always in memory it should be
EMPHASIZED that RexxRMF was INTENDED for SMALL files. How small depends
on how much memory you have. The amount of memory used will depend greatly
on the length of the keys and how many alternate indices you build.
RexxRMF allows you to specify up to five alternate indices. What this
means is that you can have multiple keys for the same file. This allows
you not only to read a file by lastname for example, but you could also
use the month in which a person was born as an alternate key index, so
that you could send the person a birthday card in time for his/her
birthday.
In addition to the primary and alternate indices RexxRMF internally
maintains a "delete" index in order to locate and keep track of deleted
records. In all, as many as seven indices could be present in memory
at any one time for a given file. Each node (record) will require about
76 + keylength bytes per record. Thus 2000 records could easily consume
170K bytes.
Your memory configuration will determine how large 'small' can be.
Data records on the other hand are loaded into memory when read/written
and immediately get discarded afterwards. Data records can have as
many fields as you wish as long as the total length of the record is
less than 65K bytes and the total number of fields is less than 65K.
With the exception of header information placed at the beginning of
each record all data records are written as null terminated ASCII text.
Thus the datafile can be viewed with a good editor, or a "filezap" type
program, the data contents should be readily apparent.
Since all indices are maintained in memory, all updates to them occur
in memory. The index file is not written to disk until the you close
the index with the CLOSE_RMF() function, the data file is updated
immediately. Their exists the possiblity that if your Amiga crashes
anytime before the index is closed, then the index and data file will
be out of sync. It is possible to rebuild the primary index from the
datafile itself. Included with the RexxRMF library are two utility
programs that can be used to rebuild the primary index.
The library functions are fairly simple to use. The functions provide
for the common (basic) file operations of adding, changing and
deleting records. The RexxRMF library passes data from the datafile
directly to variables in your ARexx program. You tell RexxRMF which
program variables are to receive data by passing to the RexxRMF library
functions the names of the variables. Data record variables are read
and written in the order you specify. The variable names must conform
to ARexx naming rules and stem/compound variables are not supported.
The function descriptions and discussion below describe the details of
using each function.
RexxRMF.Library
Record Management Functions
Eight functions are provided for managing data file records.
1). OPEN_RMF()
2). WRITE_RMF_RECORD()
3). READ_RMF_RECORD()
4). NEXT_RMF_RECORD()
5). UPDATE_RMF_RECORD()
6). UPDKEY_RMF_RECORD()
7). DELETE_RMF_RECORD()
8). CLOSE_RMF()
These functions access your ARexx program variables directly, in order
to read/write the data file. You only need to specify the record
layout and the names of the variables within the record.
The functions require two files (which are automatically created) to be
present:
1). the data file
which contains the actual data records
2). the index file
which is the file that contains the tree information.
If you create a file using these routines then it is probably best
that you use only these routines to manage them, since it is real
easy screw to up the indices.
Function Usage
To use the functions simply call them by name as you would with
any other ARexx or library function.
The functions described below require that the names of the fields
to read/write be placed in an ARexx string variable or literal,
which I will call the 'record layout string'. A simple assignment
statement accomplishes the creation of the record layout string.
eg. RECORD LAYOUT STRING
myrecordlayout = "firstname lastname phonenumber dateofbirth"
When the functions are called, the data will be read/written
in the order specified in the string. The variable names used
in the string must conform to ARexx variable name rules.
( Stem and Compound variables are not supported)
Within the record layout string you may insert 'special' variables
which will cause additional information to be returned to your
ARexx program. (see Special Symbol Variables below)
RexxRMF.Library
Record Management Function Descriptions
OPEN_RMF
Opens RMF data and index files for use.
ix = OPEN_RMF([filename] [,dup0] [,nodata])
OPEN_RMF accepts 3 arguments all optional.
Inputs:
1). filename - the name of the data file to be indexed.
If the index file does not exists a new
one will be created. The index file will
have a suffix of '.rmfindex'.
2). dup0 - numeric, specifies whether the primary
index (0) can have duplicate keys.
This argument is optional. Note this does
not apply to any of the alternate indices,
alternate indices always allow duplicates.
Code this as either 0 or 1.
0 means duplicate keys *ARE* allowed
1 means duplicate keys are not allowed
The default is 0, duplicates allowed.
2). nodata - numeric, specifies whether a data file
should be created/maintained.
Code this as either 0 or 1.
0 means create a data file
1 means do NOT create a data file
The default is 0, create a data file.
RexxRMF.Library
OPEN_RMF
eg.
ix = open_rmf(myfile)
opens/creates both a data and index file, with duplicates
ALLOWED.
ix = open_rmf(myfile,1)
opens/creates both a data and index file, with duplicates
NOT ALLOWED.
The above open calls are the ways you would normally open
the files for use.
The following open calls are also legal.
ix = open_rmf() or open_rmf("")
ix = open_rmf("",1) - specifies nodups
neither an index nor data file are created, 'ix' will
be valid and can only be used with the 'low' level
functions. The index cannot be saved since no filename
is specified. This form uses the AVL-tree for its sort/
search functions, nothing can be saved.
ix = open_rmf(myfile,1,1)
open/create an index file, duplicates NOT allowed, no
data file is opened/created. You cannot use any of the
..._RMF_RECORD functions. The index can be saved.
ix = open_rmf(myfile,0,1)
open/create an index file, duplicates allowed, no
data file is opened/created. You cannot use any of the
..._RMF_RECORD functions. The index can be saved.
The intent of the above two forms is to allow you the
ability to use RexxRMF to index your data, but leaves
the managing (reading/writing) of the data records
themselves to you.
ix = open_rmf(myfile,0,0) is the same as open_rmf(myfile)
ix = open_rmf(myfile,1,0) is the same as open_rmf(myfile,1)
The data or index files will only be created if they do not
currently exists. Otherwise the existing files are used, this
allows you to verify the index if it were rebuilt using the
the utility program 'rebuildrmfindex' described below.
OPEN_RMF will return non-null if the call succeeds.
The returned value is an ARexx 'packed' hex string.
This value is used by the other record management functions
to access the tree and the data file. Basically this value
points to global data needed by the other functions.
DO NOT manipulate this value in anyway.
RexxRMF.Library
WRITE_RMF_RECORD
Writes a new record to the data file.
x = WRITE_RMF_RECORD(ix,record,key,[{ixnum1,altkey1} ... 5 ])
WRITE_RMF_RECORD accepts up to 13 arguments, three of which are
required arguments. The other ten arguments are optional.
The ten optional arguments specify alternate index/key pairs to
be associated with the record. WRITE_RMF_RECORD will always write
the primary index (index #0)
Inputs:
1). ix - global data pointer (from OPEN_RMF())
2). record - record layout string
3). key - is an ARexx variable or literal to be used as
the key for the record. This is the key that
will be used to write the primary index (#0).
4-13). num,key - specified in pairs, these arguments identify
alternate indices and the key to be used with
the index. Up to 5 alternate pairs can be used.
The index numbers must be between 1 and 5,
The indices can be specified in any order, at
anytime. (ie. index #1 need not be used before
index #3 or #4 are used)
Be aware that additional indexes are implemented
as individual binary trees, and therefore take
as much memory (or more depending on keylength)
as the primary index.
If index 0 is specified as an alternate,
it will simply be ignored.
If the file has been created with duplicates
not allowed the write will fail, if the key
currently exists in the file.
WRITE_RMF_RECORD returns 1 if the write succeeds, otherwise 0.
RexxRMF.Library
WRITE_RMF_RECORD
eg.
1 myrecord = "firstname lastname area phonenumber"
2 name = "Kelly"
lastname = "Kelly"
firstname = "Ronnie"
area = 999
phonenumber = "555-1212"
3 x = WRITE_RMF_RECORD(ix,myrecord,name)
or x = WRITE_RMF_RECORD(ix,myrecord,lastname)
stmt. 1: defines the ARexx variable names that
will be accessed and written to the data file.
Also defines the layout of the data record, ie. the
fields will be written in the order specified. This
is important, the same order must be used to read the
data with READ_RMF_RECORD().
stmt. 2: gives 'name' the value 'Kelly', name will then be
used as the key.
stmt. 3: adds (writes) the record to the data file and updates
the primary index with the key value taken from name.
The primary index is always written.
'myrecord' specifies the names of the ARexx
variables to use, the values of these variables
are obtained by the RMF library function.
eg.
x = WRITE_RMF_RECORD(ix,myrecord,name,4,area)
this statement adds the record to the file, and updates the
primary index with the key value taken from name. Also
alternate index number 4 is updated with key value taken
from the field area. You (or the program) must now
remember that index number 4 is indexing on area.
eg.
x = WRITE_RMF_RECORD(ix,myrecord,lastname,4,area,2,firstname)
this statement adds the record to the file, updates the
primary index with the key value taken from lastname.
Two alternate indices are used index number 4 is indexing on
area, and index number 2 in indexing on firstname.
Note that in the two previous examples the indices were not used
in order, ie. indices 2 and 4 were specified, but 1 and 3 were
not. If you later decide to use indices 1 or 3 then you should
use an UPDATE_RMF_RECORD specifying indices 1 or 3 (or both).
(See UPDATE_RMF_RECORD description)
RexxRMF.Library
WRITE_RMF_RECORD
eg.
x = WRITE_RMF_RECORD(ix,myrecord,'Kelly',4,area,2,fname)
x = WRITE_RMF_RECORD(ix,myrecord,'Kelly',4,area,2,fname,3,zip)
The above sequence would cause two records to be written, the
first would have primary index value of 'Kelly', index 4 will
have the value of area, and index 2 would have the value
of firstname.
The second statement causes a second (duplicate) record to be
written with the same index values for the primary index and
indices 2 and 4. In addition a entry would be made in index
3 for the zip code.
If your intent is to start using index 3 for zipcodes then
the following sequence should be used:
WRITE_RMF_RECORD(ix,myrecord,'Kelly',4,area,2,firstname)
UPDATE_RMF_RECORD(ix,myrecord,'Kelly',4,area,2,firstname,3,zip)
With the above sequence a second record would not be added,
but an entry will be made into index 3 for the zip code.
To have all existing records indexed by a new index you have
to sequentially read through the entire file updating each
record with the new index field.
RexxRMF.Library
READ_RMF_RECORD
Read data record from file.
x=READ_RMF_RECORD(ix,ixnum,record,key,{'K'|'R'|'P'|'U'|'L'},[occur])
READ_RMF_RECORD accepts six arguments, five required plus one optional.
Inputs:
1). ix - global data pointer (from OPEN_RMF())
2). ixnum - integer specifies which index to use
this should be a numeric whos value is
between 0 and 5.
value 0 specifies the 'primary' index
value 1 - 5 specify alternate index.
3). record - record layout string
If the read is successful the ARexx variables
named in this string will be loaded with data
from the record that was read.
4). key - is an ARexx variable or literal to be used as
the record key to read (search) for.
5). rdtype - this parameter must be either K, R, P, L, or U.
If 'K' it specifies that the 'key' argument
is actually a string KEY. READ_RMF_RECORD
will read for the record with this string key,
the entire key must match, including occurence.
If 'P' it specifies that the 'key' argument
is actually a string KEY. READ_RMF_RECORD
will read for the record with this string key.
However partial matches of the key will
result in success. ie. the 'key' specifies a
sub-string anchored at the beginning of the key.
If 'R' it specifies that the 'key' argument
is actually a NUMERIC record number.
READ_RMF_RECORD will then read the record
number specified by 'key', 'key' must have a
numeric value. Reading by record number order
is the same as reading in key sorted order.
If 'L' it specifies that the 'key' argument
is actually a NUMERIC record number.
READ_RMF_RECORD will then read the record
number specified by 'key' in reverse order,
'key' must have a numeric value.
eg. if ...,'L',1) reads last record
if ...,'L',2) reads next to last record
if ...,'L',3) reads 2nd from last record
etc.
ie. 'R' reads from top to bottom (ascending)
'L' reads from bottom to top (descending)
If a subsequent NEXT_RMF_RECORD() call is made
the next to the last record is read, etc.
(see NEXT_RMF_RECORD)
If 'U' it specifies that the 'key' argument
is actually a string KEY. READ_RMF_RECORD
will read for the FIRST occurence of the key,
the 'occur' argument is ignored. Primarly
intended for reading unique keys in the file.
(see NEXT_RMF_RECORD)
6). occur - is a numeric variable or literal which
specifies which occurence of a key to read.
Multiple keys with the same value are uniquely
identified by an occurence number. (default 1)
READ_RMF_RECORD returns 1 if the read succeeds, otherwise 0.
RexxRMF.Library
READ_RMF_RECORD
eg.
1 myrecord = "firstname lastname phonenumber"
2 recordnum = 27
3 x = READ_RMF_RECORD(ix,0,myrecord,recordnum,'R')
4 if x = 1 then
5 say firstname lastname phonenumber
6 else say "Record Not Found"
stmt. 1: defines the ARexx variable names that
will be accessed and loaded with data.
stmt. 2: gives 'recordnum' the value 27
stmt. 3: reads (searches the tree) the data file for record
number 27 (value of recordnum) and returns 1 if
successful. If successful then the ARexx variables
firstname, lastname, phonenumber will contain the
contents from record 27.
eg.
x = READ_RMF_RECORD(ix,0,myrecord,27,'R')
does the same as above, read record #27.
x = READ_RMF_RECORD(ix,0,myrecord,27,'K')
search for record whose key is '27', this is NOT the
same as the 27th record. (entire key must match)
x = READ_RMF_RECORD(ix,0,myrecord,"Kel",'P')
will search for the FIRST record who's key begins with
"Kel", partial key match. ( searches are case sensitive)
x = READ_RMF_RECORD(ix,0,myrecord,"Kelly",'K',3)
will search for the THIRD occurence of a record who's
key is "Kelly". Note if less than 3 exists then the
read will fail.
x = READ_RMF_RECORD(ix,0,myrecord,"Kel",'P',4)
will search for the FOURTH occurence of a record who's
key begins with "Kel".
(partial match beginning with "Kel")
If the index (and data file) contains the keys
Adams occurence 1
Adams occurence 2
Adams occurence 3
Keller occurence 1
Kelly occurence 1
Kelly occurence 2
Kelvin occurence 1 <-- 4th key beginning with "Kel"
then
x = READ_RMF_RECORD(ix,0,myrecord,"Kel",'P',4)
will return the record for 'Kelvin'
---------
NOTE: if a data field is null, then RexxRMF will return
a string value of "(null)". This is so that it is
immediately apparent that the field contains no data.
If you want it to truly be null, then you will have
to assign it a null value again.
ie. if phonenumber = "(null)" then phonenumber = ''
RexxRMF.Library
NEXT_RMF_RECORD
Read the next record from the data file.
x = NEXT_RMF_RECORD(ix,ixnum,record)
NEXT_RMF_RECORD should only be called immediately after
a READ_RMF_RECORD has been made. NEXT_RMF_RECORD will
then return in sequential order the next record that
satisfies the most recent READ_RMF_RECORD call,
NEXT_RMF_RECORD accepts three arguments, all required.
Inputs:
1). ix - global data pointer (from OPEN_RMF())
2). ixnum - integer specifies which index to use
this should be a numeric whos value is
between 0 and 5.
value 0 specifies the 'primary' index
value 1 - 5 specify alternates.
3). record - record layout string
NEXT_RMF_RECORD returns 1 if the read succeeds, otherwise 0.
eg. If the index (and data file) contain the keys
record# 1 1Adam12 occurence 1
2 1Adam12 occurence 2
3 1Adam12 occurence 3
4 1Adam22 occurence 1
5 1Adam22 occurence 2
6 27aint11 occurence 1
7 27aint11 occurence 2
8 27aint11 occurence 3
9 27aint11 occurence 4
10 27aint11 occurence 5
11 27aint7 occurence 1
12 27aint7 occurence 2
13 3MaryAdam occurence 1
most recent most recent successive calls to
READ_RMF_RECORD READ_RMF_RECORD NEXT_RMF_RECORD
(ix,0,myrecord, ... returns return
record # record #s
...,4,'R') 4 5,6,7,8,9,10,11,12,fail
...,"27aint11",'K') 6 7,8,9,10,fail
...,"27",'P') 6 7,8,9,10,11,12,fail
...,"27",'P',6) 11 12,fail
(6th occurence of '27')
...,"1Adam12",'U') 1 4,6,11,13,fail
(starting with "1Adam12"
return all unique keys)
...,1,'L') /*reverse order*/ 13 12,11,10,9,8,7,6,5,4,3,2,1,fail
...,3,'L') 10 9,8,7,6,5,4,3,2,1,fail
RexxRMF.Library
UPDATE_RMF_RECORD
UPDATE_RMF_RECORD is used to update a record by record number.
The record number is the number of the record in key sorted
order.
eg. If your file contained two records with keys A and C,
then the record with key A is record number 1,
and the record with key C is record number 2.
If you then add a new record to the file with key B
then record A is record 1, record B becomes record 2
and record C becomes record 3.
The primary key will not be updated, so if you are updating
the *PRIMARY* key of a record you should use UPDKEY_RMF_RECORD,
and not UPDATE_RMF_RECORD. Note that alternate index keys can
be updated with UPDATE_RMF_RECORD.
x = UPDATE_RMF_RECORD(ix,record,recnum,[{ixnum1,altkey1}...])
UPDATE_RMF_RECORD accepts 13 arguments, 3 required, 10 optional.
Inputs:
1). ix - global data pointer (from OPEN_RMF())
2). record - is a string which contains the variable names.
(ie. record layout string)
3). recnum - is a numeric ARexx variable or string specifing
which record number to update.
4-13). - alternate index key pairs
these arguments must be specified in pairs
they specify the alternate indexes and the
key to be used with that index.
If not specified the alternate index retains
its current value when the record is updated.
UPDATE_RMF_RECORD returns 1 if the update succeeds, otherwise 0.
RexxRMF.Library
UPDATE_RMF_RECORD
eg.
1 myrecord = "firstname lastname phonenumber"
2 recordnum = 27
3 x = READ_RMF_RECORD(ix,myrecord,recordnum,'R')
4 phonenumber = " (219) 555-5555 "
5 x = UPDATE_RMF_RECORD(ix,myrecord,recordnum)
stmt. 1: defines the ARexx variable names that
will be accessed and written to the data file.
stmt. 2: gives 'recordnum' the value 27
stmt. 3: reads the data file for record number 27
stmt. 5: updates record 27, If successful, a NEW record
will be written with the new value for 'phonenumber'.
Note the record is read first to get the current values for
the record. Updates are done by deleting the old record and
writing the new changed record, using the current values of
of the record layout string variables.
You may at any time add additional fields to an existing record.
Simply include the new fields in the record layout string and
update the record. If you were going to add new fields to all
records, then you would probably want to use two record layout
strings. One string in the format as the record currently exists,
and the second string in the format of the new layout. Then
simply read the record with the current layout and update it with
the new layout. (Dont forget to set the values of your new fields)
RexxRMF.Library
UPDKEY_RMF_RECORD
UPDKEY_RMF_RECORD is used to update a record by key.
The primary key does not have to change, but if you will be
changing the primary key then you MUST use UPDKEY_RMF_RECORD
else the indices will get screwed up.
x = UPDKEY_RMF_RECORD(ix,record,okey,occr,nkey,[{ixnum1,altkey1}...])
UPDKEY_RMF_RECORD accepts 15 arguments, 5 required, 10 optional.
Inputs:
1). ix - global data pointer (from OPEN_RMF())
2). record - is a string which contains the variable names
(record layout string)
3). okey - the current value of the primary key
(ie. index 0)
4). occr - numeric, which occurence of the oldkey
defaults to 1
5). nkey - the new value for the primary key (index 0)
if not changing the key, then oldkey and
newkey should be the same.
6-15). - alternate index key pairs
these arguments must be specified in pairs
they specify the alternate indexes and the
key to be used with that index.
If not specified the alternate index retains
its current value when the record is updated.
UPDKEY_RMF_RECORD returns 1 if the update succeeds, otherwise 0.
eg.
1 old = 'kelly'
2 occur = 1
3 new = 'KELLY' (keys are case-sensitive)
4 x = UPDKEY_RMF_RECORD(ix,myrec,old,occur,new)
Updates the primary index (0), replacing the first
occurence of 'kelly' with 'KELLY'.
or
4 x = UPDKEY_RMF_RECORD(ix,myrec,old,occur,old)
old key value is retained
or
4 x = UPDKEY_RMF_RECORD(ix,myrec,old,occur,new,1,area,4,zip)
Updates the primary index, as well as index 1 & 4.
If indices 2,3,5 are also being used, they will retain
their current values, since the update did not specify
new ones.
RexxRMF.Library
DELETE_RMF_RECORD
DELETE_RMF_RECORD deletes records from the data file and index.
x = DELETE_RMF_RECORD(ix,ixnum,record,key,{'K'|'R'|'L'},[occur])
DELETE_RMF_RECORD accepts 6 arguments, 5 required, 1 optional.
Inputs:
1). ix - global data pointer (from OPEN_RMF())
2). ixnum - integer specifies which index to use
Note if deleting by an alternate index
the primary node and all alternate nodes
are deleted as well. This is a delete of
the record and references to the record.
3). record - record layout string
when a record is deleted the values from the
deleted record are returned in the record
layout string.
4). key - is an ARexx variable or string to be used as
the key for the record.
5). 'K' or 'R' - parameter five must be either 'K','R', or 'L'.
or 'L' same as READ_RMF_RECORD.
DELETE_RMF_RECORD(...,1,'L') del last.
DELETE_RMF_RECORD(...,2,'L') del next to last.
'P' and 'U' are not allowed..
6). occur - numeric, occurence of this key to delete
meaningfull only if deleting by key ('K').
(ie. their can only be one occurence of
a particular record number, but
multiple occurences of a key)
DELETE_RMF_RECORD returns 1 if the delete succeeds, otherwise 0.
eg.
1 myrecord = "deletedfname deletedlname deletedpnumber"
2 recordnum = 27
3 x = DELETE_RMF_RECORD(ix,0,myrecord,recordnum,'R')
stmt. 1: defines the ARexx variable names that will receieve
the values stored in the data record.
stmt. 2: gives 'recordnum' the value 27
stmt. 3: deletes record number 27, and updates the index.
If successful, the ARexx variables specified in 'myrecord'
will contain the values from the deleted record. Thus you
know what the values were before the record was deleted and
can write it back to the file if need be. Note these variables
can be any variables you wish. (ie. you can use one record
layout for reads/writes and another for deletes so as not to
disturb the contents of your current read/write variables)
RexxRMF.Library
CLOSE_RMF
This function releases the memory allocated to the tree,
so it must be called when you are done processing the file.
x = CLOSE_RMF(ix [,saveflag])
CLOSE_RMF accepts two arguments.
1). ix - global data pointer (from OPEN_RMF())
2). saveflag - numeric, optional, use a value of 0 to specify
that the index SHOULD BE SAVED.
use a value of 1 to specify that the index is
NOT TO BE SAVED.
default is 0, save the index
CLOSE_RMF returns 0 if the close succeeds, otherwise it returns a
non-zero error code.
RexxRMF.Library
ANY_ERR
ANY_ERROR
ANY_ERRORS
If any of the functions fail (return zero or NULL '0000 0000'x),
this function can be used to interrogate the library for the error
condition that caused the function to fail.
x = ANY_ERR(ix)
ANY_ERR accepts one argument.
1). ix - global data pointer (from OPEN_RMF())
Values returned:
RMF errors
4000 - RECORD_NOT_FOUND
READ_RMF_RECORD failed (record does not exist)
4100 - NO_FILE_TO_SAVE_TO
Attempt to save index file which was opened without a name.
4200 - RECORD_KEY_EXISTS
Attempt to add key to index that did not allow duplicates.
4300 - NODE_ALLOCATION_FAILED
Only occurs when memory allocation for node fails.
(means you got less than 76 bytes of memory left !!)
4400 - MAX_KEYLEN_EXCEEDED
Key length is greater than 512 bytes
4401 - ERROR_KEY_HAS_NO_LENGTH
A zero length key was used.
4600 - MAXIMUM_INDICES_USED
You should not get this error, you should get 4700 before this
one will occur. If you do get this error it means you some
how managed to get past the edit checks on index number and
the AVL algorithm itself is complaining.
4700 - INVALID_INDEX_NUMBER
Index number you specified was not valid.
Means you used an index number < 0 or > 5.
4800 - INVALID_NODE_POINTER
You should not get this error, but if you do it means a
pointer to a tree node was used that had a NULL value.
4802 - INVALID_NODE_POINTER
You should not get this error, but if you do it means a
pointer to a tree node was used that had a NULL value.
4804 - INVALID_NODE_ODDPOINTER
You should not get this error, but if you do it means a
pointer to a tree node was used that had a illegal odd value.
4806 - CANNOT_LINK_PRIMENODES
You tried to add_key() into index #0, and link it to another
'primenode' at the same time.
4900 - INVALID_RECORD_NUMBER
You used a record number with value <= 0.
5000 - INVALID_DELETE_TYPE
Record delete type was not 'K' or 'R'.
RexxRMF.Library
5001 - INVALID_READ_TYPE
Record read type was not 'K' or 'R' or 'P' or 'U'.
5100 - COULD_NOT_ADD_TO_DELETE_INDEX
A record block was deleted that has no entry in the delete
index. Simply means your file will have a delete block that
RexxRMF will know nothing about. (similar to 4300)
5200 - TREE_NODE_DELETION_FAILURE
If this occurs, call me, means the AVL algorithm aint working.
5300 - TREE_NODE_INSERTION_FAILURE
If this occurs, call me, means the AVL algorithm aint working.
5600 - FILE_READ_ERROR
An I/O error occured while reading the data file
5602 - FILE_READ_EOF
Read past end of data file
5700 - FILE_WRITE_ERROR
An I/O error occured while writing the data file
5800 - ERROR_READ_INDEX_IOERROR
An I/O error occured while reading the index file
5801 - ERROR_READ_INDEX_ZEROKEY_LEN
While loading index file encountered key with zero length.
(ie. the record had no keys, neither primary nor alterate)
Loading of the index is immediately terminated, you
probably do not want to save the index if this occurs.
5802 - ERROR_READ_INDEX_MAXKEY_LEN
While loading index encountered key which exceeded MAXKEYLEN.
5900 - ERROR_WRITE_INDEX_IOERROR
An I/O error occured while writing the index file
Dos errors
If the error number is something less than the errors above
then it indicates a DOS error (from IoErr())
103 - ERROR_NO_FREE_STORE
221 - ERROR_DISK_FULL
222 - ERROR_DELETE_PROTECTED
223 - ERROR_WRITE_PROTECTED
224 - ERROR_READ_PROTECTED
(and any others that may occur, see AmigaDos manual)
RexxRMF.Library
Special Symbol Variables
As mentioned above special variables can be used in the record layout
string to return information about the record just read or written.
The functions recognize nine special symbols, which must immediately
precede a variable name within the record layout string.
The special symbols are '@', '#', '&', '=', '<', '>', '-', '%' and '*'.
'@' - returns the record address of the record just
read/written. This is the relative byte location
of were the record begins in the data file.
'#n' - returns the occurence number of the record,
'n' is the index number.
'&n' - returns the key of the record, useful if reading by
position, 'n' is the index number.
eg. If you read record number 32, this causes the
key for record number 32 to be returned as well.
'=n' - returns the record position within the index used to
read/write the record, 'n' is the index number.
This is the position of record key in sorted order.
'<' - read only field, the fieldname following the '<' will NOT
be written to the datafile during writes/updates, but will
be loaded with data during reads.
'>' - write only field, the fieldname following the '>' will NOT
be loaded with data during reads, but will be written to the
datafile during writes/updates.
The intent of '<' and '>' was to allow a single layout string
to be used for both reading and writing.
'-' - suppresses the reading/writing of the data.
useful when you want to skip over fields in a data
record (or not change their current value).
Be particularly watchful of this when doing writes,
since the data will NOT be written to the file.
When doing a read the variable is NOT loaded with
data from the file. A field name need not follow
the '-' when reading a record.
eg. reclayout = "first last - - - address"
will cause the three fields following 'last' in the data
record to be skipped.
'%' - returns the number of fields in the data record.
For reads this is the total number of fields in the record,
NOT the number of fields which were loaded with data.
For writes this *IS* the number of fields that were actually
written, NOT the number of fields in the record layout string.
Note for reads/writes this count includes the primary key.
'*n' - returns the actual memory address of the tree node for this
record. 'n' is the index number. The value is returned as
an ARexx hex-string. This value can then be used with the
'low' level functions described below.
eg.
@anyname the '@' does not take an index number
stores the record address in the ARexx variable
'anyname'
#3anyname stores the occurence of the record key within
index 3 in the ARexx variable 'anyname'
&1anyname stores the record key from index 1 in the
ARexx variable 'anyname'
=2anyname stores the position of the record within index 2
in the ARexx variable 'anyname'
-anyname the '-' does not take an index number
skips the reading/writing of next field
(remember record layout is positional)
#anyname is the same as #0anyname, index 0 is the primary index
=anyname is the same as =0anyname, etc.
These special variables can be placed any where in the record
layout string.
eg. myrecord = " @rba firstname lastname =x0 =3x3 phonenumber"
In the above record layout string three record values and
three special variables will be returned. The special
variables are rba, x0, and x3.
success = READ_RMF_RECORD(ix,3,myrecord,phone,'K')
This READ_RMF_RECORD call uses index 3 to read for a data record.
The above READ_RMF_RECORD will return the following values:
firstname, lastname, phonenumber will be 'stuffed' with data
from the file.
In the 'myrecord' layout string @rba, =x0, =3x3 are specified
rba = physical location in the file from which the record was read
x0 = relative position of the record in the primary index
(ie. says this is the first, second, third, etc. record
in the primary index.)
x3 = relative position of the record in alternate index 3
eg. if the file contained the following three records
by primary, index 0 by index 3
rec (indexing on lastname) (indexing on phone)
location
1. 220 John Appleton 555-1232 | 555-1230 Mike Wilson
2. 908 Ron Kelly 555-1231 | 555-1231 Ron Kelly
3. 1705 Mike Wilson 555-1230 | 555-1232 John Appleton
if the read returned the record for Mike Wilson
then
rba = 1705
x0 = 3, ie. Mike Wilson is the third record
when reading by the primary index
x3 = 1, ie. Mike Wilson is the first record
when reading by index 3.
The same would be true for a WRITE_RMF_RECORD call, with the exception
that data record values are not returned. Presumably they have the same
values that were just written to the file.
eg. myrecord = " @rba firstname lastname =x0 =3x3 phonenumber"
success = WRITE_RMF_RECORD(ix,3,myrecord,phone,'K')
The values from firstname, lastname, phonenumber would be left
as they are. However, WRITE_RMF_RECORD would return to your ARexx
program the variables:
rba = location in the file where the record was written
x0 = relative position of the record in the primary index
x3 = relative position of the record in alternate index 3
RexxRMF.Library
Low Level Function Descriptions
The record management functions are built on functions which directly
manipulate the nodes of the tree. These 'low' level functions can be
used by your ARexx program as well. These functions are particularly
useful if you are simply searching for the existence of a key or record.
Some of the functions return or accept pointers to the actual node
in the tree, so be careful of when and where you use them.
Do not intermix the 'low' level ADD/UPDATE/DELETE functions with
the ...RMF_RECORD functions above. The index and data file will more
than likely get screwed up if you start adding/deleting keys behind the
back of the ...RMF_RECORD functions. The ADD/UPDATE/DELETE functions
should only be used when you are managing the reading/writing of the
data file yourself.
The 'low' level functions do NOT perform any I/O to the data file,
only the in memory tree structure is accessed. Thus when record
content is not important, and only the existence or non-existence
of a key value needs to be determined, it would be better to use
these functions.
'low' level function descriptions:
--------------------------------------------------------------------------
ADD_KEY
add_key(ix,ixnum,keydata,rba,[primenode])
Add a node into the tree with the specified key.
ix - pointer to global data
(returned by open_rmf)
ixnum - numeric, which index to use
0 = primary index
1 - 5 = alternate index
Note if 'primenode' is specified, then 'ixnum'
must be 1-5. ie. you cannot link a primenode to
another primenode.
keydata - string, key value to be inserted in tree
rba - numeric, this is the relative block address
of the record data for this key.
eg. WRITE_RMF_RECORD() calls this function passing
it the rba of where the record will be written.
primenode - pointer, optional, a primary node pointer
(ie. a node in index #0)
If specified 'primenode' must be a node in index #0,
and 'ixnum' must be > 0. (see 'ixnum' above)
The newly added key will be 'linked' to this primenode.
Thus creating alternate key paths to the same record.
eg.1
Lets say you do the following:
primenode -------> node0 = ADD_KEY(ix,0,Lastname,rba0)
its in index #0 node1 = ADD_KEY(ix,1,ZipCode,rba1)
node2 = ADD_KEY(ix,2,State,rba2)
node3 = ADD_KEY(ix,3,Employer,rba3)
node4 = ADD_KEY(ix,4,DeptNo,rba4)
node5 = ADD_KEY(ix,5,SocNum,rba5)
This results in 6 nodes being added.
Since RexxRMF keeps each index as a separate
tree, the nodes are not 'related'. What you
have is six INDEPENDENT indices on a file.
(Which is ok, if thats what you want)
If 'SocNum' is to be an alternate key for
'Lastname' the above won't do it unless
'rba5' = 'rba0'
However if you do the following
eg.2
primenode -------> node0 = ADD_KEY(ix,0,Lastname,rba0)
its in index #0 node1 = ADD_KEY(ix,1,ZipCode,rba1,node0)
node2 = ADD_KEY(ix,2,State,rba2,node0)
node3 = ADD_KEY(ix,3,Employer,rba3,node0)
node4 = ADD_KEY(ix,4,DeptNo,rba4,node0)
node5 = ADD_KEY(ix,5,SocNum,rba5,node0)
Six nodes are still added, however the
nodes 1-5 will be linked to node0. The
'rba' values used for nodes 1-5 five will
be ignored and the 'rba' will be taken
from node0. Thus all keys are pointing to
the same record, and are alternates for node0.
Note that in the first example above, if all the 'rba'
values are the same you accomplish building alternate
keys to the same record. The BIG difference is what
happens whens deletions are made.
In eg.1 deleting a key from index #5 simply deletes
that key from that index (tree).
In eg.2 deleting a key from index #5, will delete all
key references (links) to the same record. Thus, the
primenode as well as the other alternates are deleted.
Returns - pointer (ARexx hex-string) to the tree node
if successfully added, else NULL ('0000 0000'x)
--------------------------------------------------------------------------
DELETE_KEY
delete_key(ix,ixnum,keydata,occur)
Delete a tree node by key.
If the deleted key occurs multiple times than all remaining keys will
have their 'occurence' number adjusted to reflect the correct number
of occurences.
ix - pointer to global data
(returned by open_rmf)
ixnum - numeric, which index to use
keydata - string, key to delete
occur - which occurence to delete
Returns - 1 if successful, else returns zero.
--------------------------------------------------------------------------
DELETE_POS
delete_pos(ix,ixnum,pos)
Delete a tree node by relative position.
ie. If you build a tree with ten names,delete_pos(ix,0,6) will delete
the sixth name (in sorted order) from the primary index.
If the key deleted in position 'pos' occurs multiple times than all
remaining keys will have their 'occurence' number adjusted to reflect
the correct number of occurences.
ix - pointer to a IndexFile struct.
(returned by open_rmf)
ixnum - numeric, which index to use
pos - relative position of tree node to be deleted
Returns - 1 if successful, else returns zero.
--------------------------------------------------------------------------
FIND_KEY
find_key(ix,ixnum,keydata,occur)
Search tree for specified key.
Unlike LOCATE_KEY this function uses the entire key and the
occurence number must match. READ_RMF_RECORD uses this function
when you specify 'K'.
ix - pointer to global data
(returned by open_rmf)
ixnum - numeric, which index to use
keydata - pointer to keyvalue
occur - numeric, which occurence to find
Returns - pointer (ARexx hex-string) to the tree node
if successful, else returns NULL ('0000 0000'x)
--------------------------------------------------------------------------
FIND_NEXT
find_next(ix,ixnum )
Find the next occurence of a key. This function should be called
after FIND_KEY to sequentially find all occurences.
Similar to locate_next() but does not allow partial key searches.
ix - pointer to global data
(returned by open_rmf)
ixnum - numeric, which index to use
Returns - pointer (ARexx hex-string) to the tree node
if successful, else returns NULL ('0000 0000'x)
--------------------------------------------------------------------------
FIND_POS
find_pos(ix,ixnum,pos)
Search for tree node by relative position. This function allows
searches to be done by the relative positions of the nodes, ie. in
sorted order, thus random/sequential searches can be made.
READ_RMF_RECORD uses this function when you specify 'R'.
ix - pointer to global data
(returned by open_rmf)
ixnum - numeric, which index to use
pos - numeric, relative position of node
(ie. sorted order)
If pos is negative the last node in the tree
is returned. (any negative value will do)
eg.
treenode = find_pos(ix,0,-1)
would return the last node in the primary index
treenode = find_pos(ix,3,-1)
would return the last node in index #3
Returns - pointer (ARexx hex-string) to the tree node
if successful, else returns NULL ('0000 0000'x)
--------------------------------------------------------------------------
LOCATE_KEY
locate_key(ix,ixnum,keydata,occur)
Search for the specified key. This function differs from
FIND_KEY in that this function allows partial key searches.
Use this function to initially set up for calls to LOCATE_NEXT().
READ_RMF_RECORD uses this function when you specify 'P'.
ix - pointer to global data
(returned by open_rmf)
ixnum - numeric, which index to use
keydata - string, key to search for
occur - occurence number to start with
Returns - pointer (ARexx hex-string) to the tree node
if successful, else returns NULL ('0000 0000'x)
--------------------------------------------------------------------------
LOCATE_NEXT
locate_next(ix,ixnum )
Find the next sequential key in the tree. This function should
be called after LOCATE_KEY to find the next occurence of the key.
This functions differs from FIND_NEXT in that this function
allows partial keys to be searched.
ix - pointer to global data
(returned by open_rmf)
ixnum - numeric, which index to use
Returns - pointer (ARexx hex-string) to the tree node
if successful, else returns NULL ('0000 0000'x)
eg.
if the tree contains the keys
Johnson occurence 1
Lewis occurence 1
Thomas occurence 1
Thomas occurence 2
Thomas occurence 3
Thomphson occurence 1
Wilson occurence 1
p = locate_key(ix,0,"Thom",1);
will find the first occurence of Thomas
p = locate_next(ix,0);
will find the second occurence of Thomas
p = locate_next(ix,0);
will find the third occurence of Thomas
p = locate_next(ix,0);
will find the first occurence of Thomphson
p = locate_next(ix,0);
will return NULL since their no more keys beginning with
"Thom".
If the value specified for the occurence were 2 in the locate_key()
call then the first occurence of 'Thomas' would be skipped, but
all others would be found, including 'Thomphson'.
--------------------------------------------------------------------------
KEY_VALUE
key_value(treenode [,ixnum])
Returns the value of the key for the treenode, or the value of a
key from an alternate of the treenode.
treenode - pointer to a tree node, the functions
ADD_KEY(), FIND_KEY(), FIND_NEXT(), FIND_POS(),
LOCATE_KEY(), and LOCATE_NEXT() return pointers
to tree nodes.
ixnum - index number, 0-5, this parm is optional.
If not specified the value of key for the
treenode is returned.
If an index number is specified, 0-5, then the
value of the key for the specified alternate index
node is returned.
eg.
lets say primary index (0) indexes on lastname
and alternate index 2 indexes on zip code.
treenode = find_key(ix,2,"60649",1)
The above looks in alternate index 2 for the
first occurence of zip code 60649, a pointer
to the tree node is returned.
Given the treenode returned from alternate
index 2, we can get the value of the key in
the primary index (0) with the statement:
lname = key_value(treenode,0)
This returns the value of 'lastname' associated
with the treenode from alternate index 2.
Similarly the values of the other alternate
keys associated with 60649, can be obtained
with
alt1key = key_value(treenode,1)
alt3key = key_value(treenode,3)
alt4key = key_value(treenode,4)
alt5key = key_value(treenode,5)
Thus given a treenode the value of any of its
keys primary or alternate can be obtained.
The value of the zipcode is simply the value
of the key for the treenode.
zip = key_value(treenode) = 60649
or
zip = key_value(treenode,2) = 60649
since index 2 indexes on zipcode
Returns - string, the key for the node
--------------------------------------------------------------------------
KEY_COUNT
key_count(ix,ixnum,key,{ 'K' | 'P' })
Returns a count of the number treenodes with the value 'key'.
ix - pointer to global data
(returned by open_rmf)
ixnum - index number, 0-5, (0=primary)
key - string, value of key to be counted
type - string, 'K' specifies that the entire key must
match. 'P' specifies that partial matches of key
be counted as well.
eg. count = key_count(ix,3,"708",'K')
counts the number of keys in alternate index 3
with the value of "708".
Returns - numeric, number of occurences of the key
--------------------------------------------------------------------------
KEY_OCCUR
key_occur(treenode [,ixnum])
Returns the occurence number of a tree node, or the occurence number
of an alternate.
treenode - pointer to a tree node, the functions
ADD_KEY(), FIND_KEY(), FIND_NEXT(), FIND_POS(),
LOCATE_KEY(), and LOCATE_NEXT() return pointers
to tree nodes.
ixnum - index number, 0-5, this parm is optional.
If not specified the occurence number for the
treenode is returned.
If an index number is specified, 0-5, then the
occurence number for the specified alternate index
node is returned. This similar to KEY_VALUE, given
a treenode the occurence number of any of its
alternates can be obtained.
Returns - numeric, the occurence number of the node
--------------------------------------------------------------------------
KEY_RBA
key_rba(treenode)
Returns the relative block address assigned to a tree node.
treenode - pointer to a tree node, the functions
ADD_KEY(), FIND_KEY(), FIND_NEXT(), FIND_POS(),
LOCATE_KEY(), and LOCATE_NEXT() return pointers
to tree nodes.
Returns - numeric, relative block address assigned to the node.
This is the value that was stored with the ADD_KEY()
function.
--------------------------------------------------------------------------
NEXT_UNIQUE
next_unique(ix,ixnum)
Find the next unique occurence of a key. This function should be
called after FIND_KEY to sequentially find all unique occurences.
READ_RMF_RECORD uses this function when you specify 'U'.
ix - pointer to global data
(returned by open_rmf)
ixnum - numeric, which index to use
Returns - pointer (ARexx hex-string) to the tree node
if successful, else returns NULL ('0000 0000'x)
--------------------------------------------------------------------------
NODE_POS
node_pos(treenode [,ixnum])
Returns the relative position (sorted order) of a node in the tree.
treenode - pointer to a tree node, the functions
ADD_KEY(), FIND_KEY(), FIND_NEXT(), FIND_POS(),
LOCATE_KEY(), and LOCATE_NEXT() return pointers
to tree nodes.
ixnum - index number, 0-5, this parm is optional.
If not specified the position (in sorted order) of
the treenode is returned.
If an index number is specified, 0-5, then the
position of the key for the alternate index node
is returned. This is similar to KEY_VALUE,
given a treenode the relative sorted order of
any of its alternate keys can be obtained.
Returns - numeric, relative position of the node in the tree.
(ie. sorted order)
eg.
if the tree contains the keys
Johnson occurence 1
Lewis occurence 1
Thomas occurence 1
Thomas occurence 2
p --> Thomas occurence 3
Thomphson occurence 1
Wilson occurence 1
and p points to the treenode containing the key 'Thomas occurence 3'
then
x = node_pos(p);
will return 5 for the value of x, ie. Thomas occurence 3
is the 5th node (sorted, relative to all other keys) in
the tree.
--------------------------------------------------------------------------
SET_DATA
set_data(treenode,n,string)
Set data string stored with this tree node. This function allows
you to store one or two data strings with the tree node. These
strings can contain any information you wish to be associated with
the tree node. This data does not get saved in the index file.
treenode - pointer to a tree node, the functions
ADD_KEY(), FIND_KEY(), FIND_NEXT(), FIND_POS(),
LOCATE_KEY(), and LOCATE_NEXT() return pointers
to tree nodes.
n - numeric,
use 0 to specify data string 1
use 1 to specify data string 2
string - string, text to be attached to the node
Returns - always returns 1
eg.
x = set_data(treenode,1,"this is a test")
after the above is executed, the treenode will have the
text "this is a test" attached as data string 2. You
can use the GET_DATA() function to later retrieve the
data string attached to the node.
--------------------------------------------------------------------------
GET_DATA
get_data(treenode ,n)
Get data string stored with this tree node
treenode - pointer to a tree node, the functions
ADD_KEY(), FIND_KEY(), FIND_NEXT(), FIND_POS(),
LOCATE_KEY(), and LOCATE_NEXT() return pointers
to tree nodes.
n - numeric, use 0 to specify data string 1
use 1 to specify data string 2
Returns - string, the stored string data
--------------------------------------------------------------------------
RexxRMF.Library
RexxRMF Functions Summary
number = ANY_ERR(ix) or ANY_ERROR(ix) or ANY_ERRORS(ix)
ix = OPEN_RMF([filename] [,dup0] [,nodata])
true/false = WRITE_RMF_RECORD(ix,record,key,[{ixnum1,altkey1}...5])
true/false = READ_RMF_RECORD(ix,ixnum,record,key,{'K'|'R'|'P'|'U'},[occ])
true/false = NEXT_RMF_RECORD(ix,ixnum,record)
true/false = UPDATE_RMF_RECORD(ix,record,recnum,[{ixnum1,altkey1}...])
true/false = UPDKEY_RMF_RECORD(ix,record,okey,occr,nkey,[ixnum1,altkey1])
true/false = DELETE_RMF_RECORD(ix,ixnum,record,key,{'K'|'R'},[occ])
number = CLOSE_RMF(ix,saveflag)
treenode = ADD_KEY(ix,ixnum,keydata,rba)
true/false = DELETE_KEY(ix,ixnum,keydata,occ)
true/false = DELETE_POS(ix,ixnum,pos)
treenode = FIND_KEY(ix,ixnum,keydata,occ)
treenode = FIND_NEXT(ix,ixnum)
treenode = FIND_POS(ix,ixnum,pos)
string = GET_DATA(treenode,n)
number = KEY_COUNT(ix,ixnum,key,{'K' | 'P'})
number = KEY_OCCUR(treenode [,ixnum])
number = KEY_RBA(treenode)
string = KEY_VALUE(treenode [,ixnum])
treenode = LOCATE_KEY(ix,ixnum,keydata,occ)
treenode = LOCATE_NEXT(ix,ixnum)
treenode = NEXT_UNIQUE(ix,ixnum)
number = NODE_POS(treenode [,ixnum])
true = SET_DATA(treenode,n,string)
----------------------------------
true = 1, false = 0
treenode = pointer (hex string)
RexxRMF.Library
File Maintenance:
The RMF functions READ_RMF_RECORD/WRITE/UPDATE etc. perform all the
the file I/O for the data file. When a data record is deleted its
node entry in the tree is removed and the data block is marked as
deleted by storing a zero in the record length field for the record.
The RMF functions maintain a separate tree for deleted data records.
The RMF functions attempt to re-use deleted data record blocks as
much as possible.
eg. lets say you have a single record in your data file
with a total length of 500 bytes.
you update the record so that the total length is reduced to 100
bytes.
this is whats gonna happen:
1. the recordlength of the data record is set to zero
(you now have a file that has 500 bytes not used)
(UPDATE_RMF_RECORD() will always do a delete of the record,
then a write of the record)
2. an entry is made in the delete index identifing the
location of where the deleted block occurs in the file.
(this has to be at rba location zero, since their is only
one record in the file)
3. the ARexx variables specified in the record string are
accessed and the total length computed.
(this is gonna be 100 bytes)
4. the delete index is searched for a block whose size is
large enough to hold the 100 bytes. This results in location
zero (it was just deleted) being chosen.
(If no deleted block is large enough then the record will be
written at the end of the file)
5. the block at location 0 is 500 bytes, we know this from the
block length, we only need 100 bytes. The block at location
zero gets split into two blocks.
6. the first block is used to hold the 100 bytes of data, a
entry is made into the tree.
7. for the second block (balance of 400 bytes) starting at rba
location 100, a record header is built and written to the data
file. The record header will have a block length of 400, and
a record length of zero. An entry will be made into the
delete index that a delete block occurs at rba location 100.
8. you now have two records in the file, one of which is marked
as deleted.
9. Now you start writing new records to the file, so go to step
three above. Any new records written will attempt to re-use
a deleted block first, splitting it as necessary.
So whats the point, the point is eventually the deleted block
will become too small to be reuseable. Imagine what happens
when the delete index contains several (possibly hundreds)
of delete blocks scattered throughout the file, things can
get pretty fragmented. Included with the lib is a small
utility program that will reload the data file and its index,
eliminating the deleted blocks from both the data file and the
index. You will want to do this (reload) since the delete
index is loaded along with the primary and any alternates,
if the deleted records are too small to be re-used then the
memory used to hold the delete index is being wasted.
Utility Programs
rebuildrmfindex - rebuilds the index file from the data file.
Only a index file is created, data file is left
intact.
If you crash anytime before saving the index,
this program may be able to rebuild the index
file.
reloadrmfdata - removes deleted blocks from the data and index
files. A new data file and new index file are
created. For when your datafile gets really
fragmented. This program is similar to the
rebuildrmfindex program. However reloadrmfdata
will cause the locations of all the data records
to change, specifically the data records will be
written to the new data file in key sorted order.
dumprmfdata - Formatted dump of active data records.
checkrmf - display some not-so interesting statistics
about your index file.
Source 'C' code is included for all the above utility programs.
RexxRMF.Library
Implementation Internals
For those who wish to access the data file directly, since it is your
data, the record format is given below.
Data File Format:
Each record in the data file has the following format
record header + keydata + record data + 4 byte trailer
The header is
block length = reclength + 4byte trailer
(ie. keydata + record data + 4)
record length = total length of all fields in
the record (including keys).
(ie. keydata + record data)
(deleted records have reclen = 0)
field count = number of fields in record
keydata = null terminated key strings, alternate keys have
a leading 'index' identifier byte (hex F1,F2...)
record data = individual data field strings, NULL terminated,
in the order specifed by the record layout string
when the record was written with WRITE_RMF_RECORD.
trailer = 4 NULL bytes are written at the end of each record
(NOTE the block/record length includes the NULL terminators)
eg.
|header|K0,0xf1,K1,0xf2,K2,0xf3,K3,0xf4,K4,0xf5,K5|data|4nulls|
K0 = primary key null terminated
0xf1 this byte says the following key is for index 1
K1 = alternate index 1 key null terminated
0xf2 this byte says the following key is for index 2
K2 = alternate index 2 key null terminated
0xf3 this byte says the following key is for index 3
K3 = alternate index 3 key null terminated
0xf4 this byte says the following key is for index 4
K4 = alternate index 4 key null terminated
0xf5 this byte says the following key is for index 5
K5 = alternate index 5 key null terminated
The 0xf1-5 bytes are only present if an alternate is used.
It is these key values that are used by the utility programs
to rebuild an index.
eg. data file record
|record header|record keys | record data fields |terminate|
| 20 | 16 | 8 | Kelly,null | var1,null... var8,null | 4 nulls |
block len = 20
reclen = 16
#fields = 8
key = Kelly (null terminated primary key)
var1 thru var8, each var null terminated
trailer of 4 nulls
The following record is marked as deleted, it has a record length
of zero.
20 | 0 | 0 | Kelly,null | var1,null... var8,null | 4 nulls |
RexxRMF.Library
Index File Format:
Index file consist of a fixed length Index Header block
followed by a varing number of variable length blocks. These variable
blocks are the keys for the data file. (see the avl.structs.h file)
fixeddata + [prime key,alt. keys] ... [prime key,alt. keys]
|fixed || fixed index data, followed by variable length keydata ||
|header | recaddr1,blklen,type,status,keylength[0]-keylength[7],
keydatakeydatakeydatakeydatakeydatakeydatakeydatakeydata||
eg. sample index block
||header bytes|22,10,0,A,5,0,3,2,0,0kelly708IL||
|<---- Repeats for each key in the primary index ---->|
|<------------ Fixed Length DI_Info --------->| varying |
||fixed | rba|blklen|type|status|L0|L1|L2|L3|L4|L5|L6|L7| keydata ||
||header | 22 | 10 | 0 | A |5 |0 |3 |2 |0 |0 |0 |0 |kelly708IL||
L0 is length of key for index 0 (the primary index)
L1 is length of key for alternate index 1
L2 is length of key for alternate index 2, etc.
A length of zero means the index not used for this record.
keydata is scanned for the length specified in L0-L7
this keydata is NOT null-terminated, so
'kelly' = key for index 0 (L0 = 5) this is the primary key
'708' = key for index 2 (L2 = 3)
'IL' = key for index 3 (L3 = 2)
index 1,4,5 are not used for this record since L1,L4,L5=0
L6 - is not used.
L7 - is used for delete index.
if a non-zero value is in position L7 then record block is a
deleted block.
RexxRMF.Library
Limitations:
Key length - limited to 512 bytes max
Record length - limited to 65K
Alternate keys - limited to 5
File size - none, other than those imposed by the system
Memory - yes
Tree size - limited by memory
Entire tree is loaded into memory (sorry 'bout that).
Each tree node consumes approx. 76bytes + length of key.
Each alternate key is maintained as a separate tree.
You can approximate amount of memory needed:
if #recs in file = 500
average keylen = 10 bytes
mem = 500 x (76 + 10) = 43000
if alternate keys are used then
mem = mem x (number of indices)
Stack Size - possibly
Depends how big the tree gets. I suspect you will run
out of memory before this becomes a problem. Insertions
and deletions are NOT done recursively. The freeing
of tree node memory is recursive.
Tree Maint. - I made the assumption that primary use would be in
searching and retrieving, and that few deletions
would occur during processing of a file. Thus it
is possible that the tree could become unbalanced
when deleting records (or nodes).
This is not a problem, its just that the tree does
not live up to its name (balanced binary tree) after
several deletions. If you are concerned about the tree
becoming de-generate then simply close the file and
reopen it.
Note re-balancing *IS* done for insertions into
the tree.
Future Directions:
Probably none.